<template>
    <div>
        <page-layout
            title="Role"
            :subtitle="!!role.id ? 'Update' : 'Create'"
            :button-title="!!role.id ? 'Update' : 'Create'"
            @save="save"
            @cancel="back"
        >
            <v-form v-model="formValid" ref="form">
                <card title="Basic Information">
                    <v-row>
                        <v-col cols="12" sm="6" md="6">
                            <v-text-field
                                outlined
                                dense
                                v-model="role.name"
                                label="Name *"
                                :rules="[v => !!v || 'Name is required']"
                            />
                        </v-col>
                    </v-row>
                </card>
                <template v-for="([feature_name, permissions], i) in Object.entries(featureObject)">
                    <card-plain v-bind:key="i" :title="`${formatReadableString(findModuleName(feature_name))} Module → ${formatReadableString(feature_name)}`">
                        <template v-slot:header-right>
                            <v-checkbox
                                class="select-all"
                                label="Select All"
                                :hide-details="true"
                                v-model="selectAll[feature_name]"
                                @change="onFeatureSelect(feature_name, permissions, $event)"
                            />
                        </template>
                        <v-row>
                            <template v-for="(permission, j) in permissions">
                                <v-col :key="j" cols="12" sm="3" md="3">
                                    <v-checkbox
                                        v-model="permission.checked"
                                        class="select-all"
                                        :label="formatReadableString(permission.ability)"
                                        :hide-details="true"
                                        @change="checkIsSelectAll(feature_name); updateRolePermission(permission, $event)"
                                    />
                                </v-col>
                            </template>
                        </v-row>
                    </card-plain>
                </template>
            </v-form>
        </page-layout>
    </div>
</template>

<script lang="ts">
import Vue from 'vue';
import { Permission, Role } from '@/type/Permission';
import { toJS } from 'mobx';
import store from '@/store/Index';
import removeModelRelationship from '@/helpers/ObjectHelper';
import PageLayout from '@/components/PageLayout.vue';
import Card from '@/components/card/Card.vue';
import CardPlain from '@/components/card/CardPlain.vue';

/**
 * Object with featureName as keys
 * Example Output {
 *  'feature-name-1': [
 *      abilityPermission1,
 *      abilityPermission2,3,4,...
 *  ],
 * 'feature-name-2': [
 *      abilityPermission1,
 *      abilityPermission2,3,4,...
 *  ],...
 * }
 */
type FeatureObject = {
    [featureName: string]: CheckablePermission[];
}

/**
 * Object with featureName as keys
 * Example Output {
 *  'feature-name-1': true,
 *  'feature-name-2': false,...
 * }
 */
type SelectAllObject = {
    [featureName: string]: boolean;
}

type CheckablePermission = Permission & {
    checked?: boolean;
};

export default Vue.extend({
    name: 'UserPage',
    components: {
        PageLayout,
        CardPlain,
        Card,
    },
    data(): {
        tmp: boolean;
        role: Role;
        formValid: boolean;
        role_permission: CheckablePermission[];

        selectAll: SelectAllObject;
        featureObject: FeatureObject;
        } {
        return {
            tmp: true,
            role: {
                name: '',
            },
            formValid: false,
            role_permission: [],

            selectAll: {},
            featureObject: {},
        };
    },
    computed: {
        getId(): string | number {
            return this.role.id || this.$route.params.id;
        },
        getPermissions(): Permission[] {
            return toJS(store.permission.permissions);
        },
    },
    async mounted() {
        store.loader.run(async () => {
            await Promise.all([
                store.permission.getPermissions({ perPage: Number.MAX_SAFE_INTEGER }),
            ]);
            await this.fetch(this.getId as number);
        });
    },
    methods: {
        validate() {
            return (this.$refs.form as any).validate();
        },
        initialFeatureObject() {
            this.featureObject = this.getPermissions.reduce((obj: FeatureObject, permission): FeatureObject => {
                if (!obj[permission.feature_name]) {
                    obj[permission.feature_name] = [];
                }
                obj[permission.feature_name].push(permission);
                return obj;
            }, {});
        },
        initialSelectedPermission(permissions: Permission[]) {
            this.role_permission = this.getPermissions.map((permission: CheckablePermission): CheckablePermission => {
                const hasNotPermission = permissions.map((personPermission) => personPermission.id).indexOf(permission.id) === -1;
                permission.checked = !hasNotPermission;
                return permission;
            });
        },
        initialSelectAll() {
            this.selectAll = Object.keys(this.featureObject).reduce((obj: SelectAllObject, featureName) => {
                obj[featureName] = (this.featureObject[featureName].filter((p) => !p.checked).length === 0);
                return obj;
            }, {});
        },
        async fetch(id: number | string) {
            this.initialFeatureObject();
            this.initialSelectAll();
            this.initialSelectedPermission([]);

            if (!this.getId || Number.isNaN(parseInt(this.getId as string, 10))) return;
            const role = await store.permission.getRole(id as number);
            const permissions = role.permissions as Permission[];
            this.initialSelectedPermission(permissions);
            this.role = removeModelRelationship(role);
        },
        async save() {
            const roles = this.role_permission.filter((p) => p.checked).map((p) => p.id as number);
            if (!this.validate() || roles.length === 0) {
                store.alert.show('Please ensure to fill in the required fields', 'warning');
                return;
            }

            const isCreate = !this.role.id;

            store.loader.run(async () => {
                const newRole = await store.permission.saveRole(this.role, roles);
                this.role.id = newRole.id;
                await store.user.logoutAllUser();
                store.alert.show(`The role information are ${isCreate ? 'created' : 'updated'} successfully!`, 'success');
                this.back();
            });
        },
        back() {
            this.$router.back();
        },
        async onFeatureSelect(featureName: string, permissions: Permission[], checked: boolean) {
            this.featureObject[featureName] = permissions.map((p: CheckablePermission) => {
                p.checked = checked;
                return p;
            });
        },
        checkIsSelectAll(featureName: string) {
            this.selectAll[featureName] = (this.featureObject[featureName].filter((p) => !p.checked).length === 0);
        },
        updateRolePermission(permission: CheckablePermission, checked: boolean) {
            const foundPermission = this.role_permission.find((p) => p.id === permission.id) as CheckablePermission;
            foundPermission.checked = checked;
        },
        formatReadableString(text?: string) {
            if (!text) return '';
            return text.replace(/-/g, ' ')
                .split(' ')
                .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
                .join(' ');
        },
        findModuleName(featureName: string) {
            const permission = this.getPermissions.find((p) => p.feature_name === featureName) as Permission;
            const moduleName = permission.module_name;
            return moduleName;
        },
    },
});
</script>
<style scoped>
    .select-all,
    .select-all .v-input__slot {
        margin: 0;
    }

    .select-all {
        margin-right: .618rem;
    }
</style>
